/*
 *所有关于Aps_SalesOrder类的业务代码应在此处编写
*可使用repository.调用常用方法，获取EF/Dapper等信息
*如果需要事务请使用repository.DbContextBeginTransaction
*也可使用DBServerProvider.手动获取数据库相关信息
*用户信息、权限、角色等使用UserContext.Current操作
*Aps_SalesOrderService对增、删、改查、导入、导出、审核业务代码扩展参照ServiceFunFilter
*/
using DGZImp.Core.BaseProvider;
using DGZImp.Core.Extensions.AutofacManager;
using DGZImp.MdsProject.DomainModels;
using System.Linq;
using DGZImp.Core.Utilities;
using System.Linq.Expressions;
using DGZImp.Core.Extensions;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using DGZImp.ApsProject.IRepositories;
using DGZImp.ApsProject.Dtos;
using DGZImp.Core.DbSqlSugar;
using DGZImp.BasProject.DomainModels;
using DGZImp.Core.Exceptions;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using System;
using System.Collections.Generic;
using System.Reflection.Emit;

namespace DGZImp.ApsProject.Services
{
    public partial class Aps_SalesOrderService
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IAps_SalesOrderRepository _repository;//访问数据库

        [ActivatorUtilitiesConstructor]
        public Aps_SalesOrderService(
            IAps_SalesOrderRepository dbRepository,
            IHttpContextAccessor httpContextAccessor
            )
        : base(dbRepository)
        {
            _httpContextAccessor = httpContextAccessor;
            _repository = dbRepository;
            //多租户会用到这init代码，其他情况可以不用
            //base.Init(dbRepository);
        }

        /// <summary>
        /// 获取订单排产进度列表
        /// </summary>
        /// <param name="inDto"></param>
        /// <returns></returns>
        public async Task<ApsProgressOutDto> GetProgressPages(ApsProgressInDto inDto)
        {
            ApsProgressOutDto data = new ApsProgressOutDto();

            //工艺信息
            List<ConfigSeq> seqRouteList = await getRouteList();

            #region 获取工单信息
            var moDatasQuery = Db.Set<Aps_SalesOrder>()
                .LeftJoin<Bas_Material>((a, b) => b.MaterialId == a.MaterialId && a.IsValid == 1 && b.IsValid == 1)
                .LeftJoin<Bas_Customer>((a, b, c) => a.CustomerId == c.CustomerId && c.IsValid == 1)
                .LeftJoin<Aps_Mo>((a, b, c, d) => a.SalesOrderId == d.SalesOrderId && d.IsValid == 1)
                .WhereIF(inDto != null && inDto.orderType != null, (a, b, c) => a.OrderType == inDto.orderType)
                .WhereIF(inDto != null && inDto.materialName != null, (a, b, c) => b.MaterialName.Contains(inDto.materialName))
                .WhereIF(inDto != null && inDto.orderNo != null, (a, b, c) => a.OrderNo.Contains(inDto.orderNo))
                .Where((a, b, c, d) => d.IsSchedule == 1 && d.Status != 8 && d.Status != 1)
                .Select((a) => new
                {
                    a.SalesOrderId,
                })
                .Distinct();
            //查询总数
            data.total = await moDatasQuery.CountAsync();

            //分页查询
            var salesOrderIds = await moDatasQuery
                .Skip((inDto.limit - 1) * inDto.rows)
                .Take(inDto.rows)
                .Select(a => a.SalesOrderId)
                .ToListAsync();

            var moDatas = await Db.Set<Aps_Mo>()
                .LeftJoin<Aps_SalesOrder>((a, b) => a.SalesOrderId == b.SalesOrderId && b.IsValid == 1 && a.IsValid == 1)
                .LeftJoin<Bas_Material>((a, b, c) => c.MaterialId == b.MaterialId && c.IsValid == 1 && c.IsValid == 1)
                .LeftJoin<Bas_Customer>((a, b, c, d) => d.CustomerId == b.CustomerId && d.IsValid == 1)
                .Where((a, b, c, d) => a.IsSchedule == 1 && a.Status != 8 && a.Status != 1)
                .Where((a, b, c, d) => salesOrderIds.Contains(b.SalesOrderId))
                .Select((a, b, c, d) => new OrderInfoTemp
                {
                    salesOrderId = b.SalesOrderId,
                    orderNo = b.OrderNo,
                    orderType = b.OrderType,
                    materialId = b.MaterialId,
                    materialName = c.MaterialName,
                    contractDate = b.ContractDate,
                    orderDate = b.OrderDate,
                    customerFullName = d.CustomerFullName,
                    productsCode = a.ProductsCode,
                    moCode = a.MoCode,
                    lotNo = a.LotNo,
                    routeControlState = 0,
                    requiredDate = b.RequiredDate,
                    orderStatus = b.OrderStatus,
                    totalQty = b.TotalQty,
                    orderPriority = b.OrderPriority,
                    modifyTime = b.ModifyTime
                }).ToListAsync();

            //获取工单信息工序配置id
            foreach (var mo in moDatas)
            {
                //分割产品信息如：N025BM003/配粉
                var ProductsArr = mo.productsCode.Split('/');
                if (ProductsArr.Length != 2)
                {
                    continue;
                }
                //在页面中匹配工艺时使用
                mo.groupId = seqRouteList.FirstOrDefault(t => t.name == ProductsArr[1])?.groupId;
            }
            #endregion

            #region 判断状态
            //状态0：未开始，1：生产中，2：生产完成，3：延迟生产，4：延迟生产完成
            //查询计划工单信息Aps_DistributedTask 
            foreach (var mo in moDatas)
            {
                //计划任务的工单
                var distributedTask = await Db.Set<Aps_DistributedTask>()
                    .Where(t => t.SalesOrderId == mo.salesOrderId && mo.moCode == t.MoCode && t.IsValid == 1).FirstOrDefaultAsync();
                if (distributedTask != null)
                {
                    //正在生产的工单
                    var productionTask = await Db.Set<Mds_ProductionTask>()
                        .Where(t => t.GroupId == mo.groupId && t.WorkOrder == mo.moCode && t.IsValid == 1).FirstOrDefaultAsync();
                    //如果有生产任务，则可以判断后续任务状态
                    if (productionTask != null)
                    {
                        //判断工单的状态
                        //计划和实际的开始时间都有值则表示开始生产
                        if (distributedTask.StartTime.HasValue && productionTask.BeginTime.HasValue)
                        {
                            //如果日期相同则开始生产，否则为延迟生产
                            mo.routeControlState = distributedTask.StartTime.Value.Date >= productionTask.BeginTime.Value.Date ? 1 : 3;

                            // 计划和实际的结束时间都有值则表示已生产完成
                            if (productionTask.EndTime.HasValue && distributedTask.EndTime.HasValue)
                            {
                                //如果日期相同则生产完成，否则为延迟生产完成
                                mo.routeControlState = distributedTask.EndTime.Value.Date >= productionTask.EndTime.Value.Date ? 2 : 4;
                            }
                        }
                    }
                }
            }
            #endregion

            #region 整理数据返回
            var moGroup = moDatas.GroupBy(t => t.orderNo);
            List<OrderInfo> returnOrderInfo = new List<OrderInfo>();
            foreach (var mos in moGroup)
            {
                //整理基本数据
                OrderInfo temp = new OrderInfo
                {
                    orderType = mos.FirstOrDefault().orderType,
                    orderNo = mos.Key,
                    materialName = mos.FirstOrDefault().materialName,
                    materialId = mos.FirstOrDefault().materialId,
                    customerFullName = mos.FirstOrDefault().customerFullName,
                    contractDate = mos.FirstOrDefault().contractDate,
                    orderDate = mos.FirstOrDefault().orderDate,
                    salesOrderId = mos.FirstOrDefault().salesOrderId,
                    requiredDate = mos.FirstOrDefault().requiredDate,
                    orderStatus = mos.FirstOrDefault().orderStatus,
                    totalQty = mos.FirstOrDefault().totalQty,
                    orderPriority = mos.FirstOrDefault().orderPriority,
                    modifyTime = mos.FirstOrDefault().modifyTime
                };

                //整理工单状态的数据
                //同一个订单的工单的步骤有可能会重复
                var moConfigGroup = mos.ToList().GroupBy(t => t.productsCode);
                foreach (var moConfigs in moConfigGroup)
                {
                    //取大的一个状态
                    var moConfig = moConfigs.OrderByDescending(t => t.routeControlState).FirstOrDefault();
                    Random random = new Random();

                    if (moConfig.groupId.HasValue)
                    {
                        temp.configState.Add(new ConfigStateDto
                        {
                            groupId = moConfig.groupId.Value,
                            // 生成 0 到 4 之间的随机数-测试用
                            //routeControlState = random.Next(0, 5),
                            routeControlState = moConfig.routeControlState
                        });
                    }
                }

                //整理生产数据和计划数据
                var distributedTask = await Db.Set<Aps_DistributedTask>()
                    .Where(t => t.SalesOrderId == temp.salesOrderId && t.IsValid == 1).ToListAsync();
                if (distributedTask != null)
                {
                    //计划的结束时间为 计划产出日期
                    temp.plannedOutputDate = distributedTask.Max(t => t.EndTime);

                    //投料批号为空的为第一道工序
                    var firstMo = distributedTask.FirstOrDefault(t => string.IsNullOrEmpty(t.PreMoCode));
                    if (firstMo != null)
                    {
                        //获取这道工序的实际生产任务数据
                        var productionTaskData = await Db.Set<Mds_ProductionTask>()
                        .Where(t => t.WorkOrder == firstMo.MoCode && t.IsValid == 1).FirstOrDefaultAsync();
                        if (productionTaskData != null)
                        {
                            //投产日期为第一道工序的开始时间
                            temp.productionDate = productionTaskData.CreateTime;
                        }
                    }
                }
                returnOrderInfo.Add(temp);
            }

            data.orders = returnOrderInfo;
            data.configSeq = seqRouteList;
            #endregion

            return data;
        }

        /// <summary>
        /// 工艺信息-取取最大公约数（如果有重复，取排序的最大值）
        /// </summary>
        /// <returns></returns>
        /// <exception cref="AppException"></exception>
        private async Task<List<ConfigSeq>> getRouteList()
        {
            //获取工艺信息
            var routeData = await Db.Set<Bas_RouteName>()
                .LeftJoin<Bas_RouteControl>((a, b) => a.RouteNameId == b.RouteNameId && a.IsValid == 1)
                .LeftJoin<Bas_GroupConfig>((a, b, c) => b.CurrentGroupId == c.GroupId && c.IsValid == 1)
                .Select((a, b, c) => new
                {
                    name = c.GroupName,
                    seq = b.StepSequence,
                    code = c.GroupCode,
                    groupId = c.GroupId
                }).ToListAsync();

            if (routeData == null)
            {
                throw new AppException("未查询到工艺信息，请核对数据！");
            }
            //按照seq排序，由小到大排序
            var seqRouteList = routeData
                .GroupBy(t => t.name)
                .Select(g => new ConfigSeq
                {
                    code = g.OrderByDescending(t => t.seq).First().code,
                    name = g.Key,
                    seq = g.Max(t => t.seq),
                    groupId = g.OrderByDescending(t => t.seq).First().groupId
                })
                .OrderBy(t => t.seq)
                .ToList();
            return seqRouteList;
        }

        public async Task<List<OrderDetail>> GetOrderDetail(string groupName, string orderId)
        {
            List<ConfigSeq> seqRouteList = await getRouteList();
            var tasks = await Db.Set<Aps_DistributedTask>()
                .LeftJoin<Bas_Machine>((a, b) => b.MachineId == a.MachineId && b.IsValid == 1 && b.IsValid == 1)
                .Where((a, b) => a.SalesOrderId == long.Parse(orderId))
                .Where((a,b)=>a.ProductsCode.Contains("/"+groupName))
                .OrderBy((a,b)=>a.StartTime)
                .Select((a, b) => new OrderDetail
                {
                    MachineCode = b.MachineCode,
                    MachineName = b.MachineName,
                    MoCode = a.MoCode,
                    ProductsCode = a.ProductsCode,
                    planStart = a.StartTime,
                    planEnd = a.EndTime,
                    planQty = a.Qty
                }).ToListAsync();
            //获取当前状态
            //查询计划工单信息Aps_DistributedTask 
            foreach (var task in tasks)
            {
                var ProductsArr = task.ProductsCode.Split('/');
                if (ProductsArr.Length != 2)
                {
                    continue;
                }
                var groupId = seqRouteList.FirstOrDefault(t => t.name == ProductsArr[1])?.groupId;

                //计划任务的工单
                var distributedTask = await Db.Set<Aps_DistributedTask>()
                    .Where(t => t.SalesOrderId == long.Parse(orderId) && task.MoCode == t.MoCode && t.IsValid == 1).FirstOrDefaultAsync();
                if (distributedTask != null)
                {
                    task.PreMoCode = distributedTask.PreMoCode;
                    //正在生产的工单
                    var productionTask = await Db.Set<Mds_ProductionTask>()
                        .Where(t => t.GroupId == groupId && t.WorkOrder == task.MoCode && t.IsValid == 1).FirstOrDefaultAsync();
                    //如果有生产任务，则可以判断后续任务状态
                    if (productionTask != null)
                    {
                        task.productionStart = productionTask.BeginTime;
                        task.productionEnd = productionTask.EndTime;
                        task.producedQty = productionTask.FinishedQty;
                        
                        //判断工单的状态
                        //计划和实际的开始时间都有值则表示开始生产
                        if (distributedTask.StartTime.HasValue && productionTask.BeginTime.HasValue)
                        {
                            //如果日期相同则开始生产，否则为延迟生产
                            task.routeControlState = distributedTask.StartTime.Value.Date >= productionTask.BeginTime.Value.Date ? 1 : 3;

                            // 计划和实际的结束时间都有值则表示已生产完成
                            if (productionTask.EndTime.HasValue && distributedTask.EndTime.HasValue)
                            {
                                //如果日期相同则生产完成，否则为延迟生产完成
                                task.routeControlState = distributedTask.EndTime.Value.Date >= productionTask.EndTime.Value.Date ? 2 : 4;
                            }
                        }
                    }
                }
            }
            return tasks;
        }
    }
}
